﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Reflection;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using System.Windows.Input;
using XWord.WPF.ApplicationFramework.Threads;
using XWord.WPF.ApplicationFramework.Extensions;
using System.Windows.Media;
namespace XWord.WPF.ApplicationFramework.Validations
{

    public class PriorityValidationError 
    {
       
        public int Priority { get; set; }

        public Guid ValidatorKey { get; set; }

        public string ErrorContent { get; set; }
    }

    public abstract partial class Validator : Control, IValidator
    {
        static Validator()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Validator), new FrameworkPropertyMetadata(typeof(Validator)));
        }

        public Validator()
        {
            Key = Guid.NewGuid();
        }

        private static void InitializeValidatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Validator validator = d as Validator;
            if ((bool)e.NewValue)
                validator.LoadOnce(validator.DoValidate, () => { return validator.FindAncestor<Window>() != null; });
        }

        private int count = 0;
        private void CompareAncestorCount()
        {
            if (count == 0) count = this.FindNullAncestorCount();
            UIThread.BeginInvoke(() =>
            {
                int currentCount = 0;
                var visual = this as DependencyObject;
                while (visual != null)
                {
                    visual = VisualTreeHelper.GetParent(visual);
                    currentCount++;
                    if (currentCount > count) visual = null;
                }
                if (currentCount > count)
                {
                    DoValidate();
                    //this.Loaded -= InitializeLoaded;
                }
            });
        }

        protected void DoValidate()
        {
            if (HasSourceContext)
            {
                var tempValid = IsValid;
                IsValid = Validate();
                if (tempValid == IsValid && IsValid == false)
                {
                    ValidPropertyChanged();
                }
            }
            else
            {
                IsValid = true;
            }
        }

        public int Priority { get; set; }

        private void ValidPropertyChanged()
        {
            var errors = GetErrors(Source);
            if (IsValid)
                RemoveError();
            else
            {
                AddError();
                ShowToolTip();
            }
        }

        private void RemoveError()
        {
            var errors = GetErrors(Source);
            var error = errors.Where(e => e.ValidatorKey == this.Key).ToArray();
            if (error.Length > 0)
                errors.Remove(error[0]);
        }

        private void AddError()
        {
            var errors = GetErrors(Source);
            var validationError = new PriorityValidationError() { Priority = this.Priority, ErrorContent = ErrorMessage, ValidatorKey = this.Key };
            if (!errors.Any(e => e.ValidatorKey == this.Key))
                errors.Add(validationError);
        }

        private DispatcherTimer timer;
        private ToolTip tooltip;

        private void ShowToolTip()
        {
            if (tooltip == null)
            {
                tooltip = new ToolTip();
              //  tooltip.Style = Application.Current.FindResource("ErrorToolTip") as Style;
                //if (ToolTipStyle == null)
                //{
                //    tooltip.Style = ToolTipStyle;
                //}
                tooltip.StaysOpen = true;
                timer = new DispatcherTimer();
                timer.Interval = TimeSpan.FromSeconds(1.5);
                tooltip.PlacementTarget = Source;
                tooltip.Placement = PlacementMode.Right;
                timer.Tick += delegate
                {
                    tooltip.IsOpen = false;
                    timer.Stop();
                };
            }

            tooltip.Content = GetErrors(Source).OrderBy(e => e.Priority).First().ErrorContent;
            if (tooltip.Content != null)
            {
                tooltip.IsOpen = true;
                timer.Start();
            }
        }

        public Guid Key { get; set; }
        private DateTime gapTime = DateTime.MinValue;

        private void SetSourceFromProperty()
        {
            if (Validator.GetIsFirstVisit(this))
            {
                var expression = this.GetBindingExpression(PropertyProperty);
                if (expression != null && this.Source == null)
                    this.SetValue(Validator.SourceProperty, expression.DataItem as FrameworkElement);
            }
        }

        protected static void ValidPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Validator validator = d as Validator;
            validator.SetSourceFromProperty();

            if (validator.HasSourceContext)
            {
                UIThread.BeginInvoke(() =>
                {
                    var isValid = validator.IsValid;
                    validator.IsValid = validator.Validate();
                    if (!isValid && !validator.IsValid && !Validator.GetIsFirstVisit(validator))
                    {
                        validator.GapValidate();
                    }
                }, DispatcherPriority.Normal);
            }
            else
            {
                validator.IsValid = true;
            }
            Validator.SetIsFirstVisit(validator, false);
        }

        /// <summary>
        /// Get Whether Source has DataContext
        /// </summary>
        private bool HasSourceContext
        {
            get
            {
                return this.EnableNotSourceContext || (!this.EnableNotSourceContext && this.Source.DataContext != null);
            }
        }

        private GapDispatcherTimer gapValidateTimer=null;

        /// <summary>
        /// 当验证未通过间隔时间显示错误信息
        /// </summary>
        public double GapValidateSecond {get;set;}

        private void GapValidate()
        {
            if (gapValidateTimer == null) gapValidateTimer = new GapDispatcherTimer();
            gapValidateTimer.Invoke(GapValidateSecond, ValidPropertyChanged);
        }

        public static void TriggerElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Validator validator = d as Validator;

            if (Validator.GetIsFirstVisit(validator.TriggerElement))
            {
                validator.RegisterTriggerValidationEvent();
                Validator.SetIsFirstVisit(validator.TriggerElement, false);
            }
            
            Validator.GetValidators(validator.TriggerElement).Add(validator);
        }

        private void RegisterTriggerValidationEvent()
        {
            TriggerElement.PreviewMouseLeftButtonDown += TriggerElementValidate;
        }

        private void CancelTriggerValidationEvent()
        {
            TriggerElement.PreviewMouseLeftButtonDown -= TriggerElementValidate;
        }

        private void TriggerElementValidate(object sender, MouseButtonEventArgs e)
        {
            TriggerElement.Focus();
            var errList = new List<bool>();
            foreach (var item in Validator.GetValidators(this.TriggerElement))
            {
                item.DoValidate();
                errList.Add(item.IsValid);
            }
            e.Handled = errList.Any(err => err == false);
        }

        private static void TriggerTypePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            FrameworkElement target = sender as FrameworkElement;
            UIThread.BeginInvoke(() =>
            {
                var validators = Validator.GetValidators(target);
                if (validators.Count == 0) return;
                var triggerType = (TriggerType)args.NewValue;
                validators[0].CancelTriggerValidationEvent();
                if (triggerType == TriggerType.Trigger)
                    validators[0].RegisterTriggerValidationEvent();
            });
        }

        private static void OnValidateTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement outer = d as FrameworkElement;
            FrameworkElement innter = e.NewValue as FrameworkElement;
            if (innter != null)
            {
                Validator.SetTriggerType(innter, TriggerType.Store);
                new ExterValidateBehavior(outer, innter);
            }
        }
        class ExterValidateBehavior
        {
            private FrameworkElement _outerElement;
            private FrameworkElement _innerElement;
            public ExterValidateBehavior(FrameworkElement outerElement, FrameworkElement innerElement)
            {
                _outerElement = outerElement;
                _innerElement = innerElement;
                _outerElement.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(_outerElement_PreviewMouseLeftButtonDown);
            }

            void _outerElement_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                _outerElement.Focus();
                var errList = new List<bool>();
                foreach (var item in Validator.GetValidators(_innerElement))
                {
                    item.DoValidate();
                    errList.Add(item.IsValid);
                }
                e.Handled = errList.Any(err => err == false);
            }
        }

        public virtual string ErrorMessage { get; set; }

        #region IValidator Members

        public abstract bool Validate();
        public virtual void SetErrorMessage(){}

        private void SetSourceHasError(bool val)
        {
            Source.SetValue(sourceHasErrorPropertyKey, val);
        }

        private DependencyPropertyKey sourceHasErrorPropertyKey
        {
            get
            {
                return typeof(System.Windows.Controls.Validation).
                  GetField("HasErrorPropertyKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as DependencyPropertyKey;
            }
        }

        private void AddTextBoxErrResource()
        {
            Style style = new Style(typeof(TextBox));
            Trigger trigger = new Trigger() { Property = Validation.HasErrorProperty, Value = true };
            Setter triggerSetter = new Setter(Control.ToolTipProperty, ErrorMessage);
            trigger.Setters.Add(triggerSetter);
            style.Triggers.Add(trigger);
            Source.Resources.Add(typeof(TextBox), style);
        }

        public virtual void SetErrorStyleOrTemplate()
        {
            AddTextBoxErrResource();
        }

        #endregion
    }
}
